﻿//
// <author>David Nohejl</author>
//
using System;
using WPFTrees.DataStructures;
using WPFTrees.Parser;
using System.Windows.Controls;
using System.Windows;
using System.Windows.Shapes;
using System.Windows.Media;

namespace WPFTrees.Layouts
{
    /// <summary>
    /// Layout ktery rozmisti uzly na soustrednych kruznic. 
    /// </summary>
    public class RadialLayout : LayoutBase,ISupportGroups,ICanUseBranchLengths,
        ISupportUnrooting
    {
        // polomer kruznice na ktere jsou umisteny uzly ve stejne hloubce
        double radius = 50.0; 
        
        TreeInfo<NewickNode> _info;

        /// <summary>
        /// Vytvori novou instanci RadialLayout.
        /// </summary>        
        public RadialLayout()
        {            
            _branchDrawer = new StraightLineBranchDrawer(Brushes.Black);
            _isRooted = false;
        }

        double unit = 0.0;

        /// <inheritdoc />
        public override void ExecuteLayout(Canvas canvas, NTree<NewickNode> tree)
        {
            _info = tree.Info;
            _tree = tree;
            double minDimension = Math.Min(canvas.RenderSize.Height,
                                        canvas.RenderSize.Width) / 2;
            unit = minDimension / _tree.Info.MaxDepthInUnits;

            NTreeNode<NewickNode> root = tree.GetRoot();
            // umisti koren do stredu canvasu
            Point rootPos = new Point(canvas.RenderSize.Width / 2,
                                      canvas.RenderSize.Height / 2);
            
            // rozhaze prvni uroven

            //pocatecni pootoceni
            double alpha = 0; 
            // pootoceni jednoho syna
            double step = Math.PI * 2 / root.CountImmediateChildren; 

            NewickNode node = root.Value();
            // umisteni korenu
            Canvas.SetLeft(node.VisualElement, rootPos.X);
            Canvas.SetTop(node.VisualElement,rootPos.Y);

            // schova koren pokud kreslime nezakoreneny strom
            node.VisualElement.Visibility = IsRooted 
                ? Visibility.Visible 
                : Visibility.Collapsed;

            // schovani/zobrazeni labelu                            
            Tools.TreeTools.DetermineLabelVisibility(node);

            // TODO: neiteruj pres vsechny 100 root.Children!
            foreach (NTreeNode<NewickNode> item in root.Children)
            {
                if (item == null) continue;
                NewickNode itemNode = item.Value();
                
                double multiplyRadius = (bUseBranchLenghts && itemNode.BranchLenght.HasValue)
                    ? unit * itemNode.BranchLenght.Value 
                    : radius;
                //pozice prvniho syna
                double newRootX = rootPos.X + multiplyRadius * Math.Cos(alpha);
                double newRootY = rootPos.Y + multiplyRadius * Math.Sin(alpha);
                double alphaInDegs = alpha / (Math.PI * 2.0) * 360.0;

                // pozice hrany mezi korenem a synem                
                Point newPos = new Point(newRootX, newRootY);

                _branchDrawer.PositionBranch(itemNode.BranchToParentElement,
                    node,itemNode,rootPos, newPos);
                
                // rekurze
                double newLeftInDegs = (alpha - step / 2.0) / (Math.PI * 2.0) * 360.0;
                double newRightInDegs = (alpha + step / 2.0) / (Math.PI * 2.0) * 360.0;
                
                 ExecuteLayoutRecursive(item, newPos, newLeftInDegs, 
                    newRightInDegs, 1);
                
                // posun pro dalsiho syna
                alpha += step;
            }

            
        }

        /// <summary>
        /// Jeden krok rekurzivniho pozicovani uzlu.
        /// </summary>
        /// <param name="root">koren podstromu</param>
        /// <param name="rootPos">pozice korenu podstromu</param>
        /// <param name="angleLeft">velikost uhlu leveho okraje vysece pro 
        /// podstrom. Ve stupnich.</param>
        /// <param name="angleRight">velikost uhlu praveho okraje vysece pro 
        /// podstrom. Ve stupnich</param>
        /// <param name="level">Uroven zanoreni.</param>
        void ExecuteLayoutRecursive(NTreeNode<NewickNode> root, Point rootPos, 
            double angleLeft, double angleRight, int level)
        {
            NewickNode node = root.Value();
            FrameworkElement element = node.VisualElement;

            // nastavi pozici korene
            Canvas.SetLeft(element, rootPos.X);
            Canvas.SetTop(element, rootPos.Y);

            // nastavi viditelnost labelu
            Tools.TreeTools.DetermineLabelVisibility(node);
            
            if (root.CountImmediateChildren == 0)
            {
                //leaf
                return;
            }

            if (root.Value().IsCollapsed)
            {
                // zabaleny podstrom
                return;
            }

            level++;
            // uhel pro jednoho syna
            double alpha = (angleRight-angleLeft) / (root.CountImmediateChildren);

            double multiplyRadius = (bUseBranchLenghts && node.BranchLenght.HasValue) 
                ? unit * node.BranchLenght.Value 
                : radius;

            //pozice prvniho syna
            double newRootX = rootPos.X + multiplyRadius;
            double newRootY = rootPos.Y;
            
            Point newRootPos = new Point(newRootX, newRootY);
            
            #region group
            if (root.Value().GroupInfo != null)
            {

                double leftAng = angleLeft + 0 * alpha /*+ (alpha) / 2*/;
                double rightAng = angleLeft + (root.CountImmediateChildren) * alpha ;

                UpdateGroupShape(root,
                    rootPos,
                    new Point(rootPos.X,rootPos.Y),
                    leftAng, rightAng);
            }
            #endregion    
            
            for(int i=0;i<root.CountImmediateChildren;i++)
            {
                NTreeNode<NewickNode> item = root.Children[i];
                NewickNode itemNode = item.Value();
                                
                multiplyRadius = (bUseBranchLenghts && itemNode.BranchLenght.HasValue) 
                    ? unit * itemNode.BranchLenght.Value 
                    : radius;
                newRootX = rootPos.X + multiplyRadius;
                newRootY = rootPos.Y;
                newRootPos = new Point(newRootX, newRootY);

                double otoceniSyna = angleLeft + i*alpha + (alpha) / 2.0;
                
                RotateTransform transform = new RotateTransform(otoceniSyna, 
                    rootPos.X, rootPos.Y);
                Point itemRootPos = transform.Transform(newRootPos);
               
                double newLeft = angleLeft + alpha * i;
                double newRight = newLeft + alpha;
                       
                // nakresli spojnici mezi korenem a synem         
                _branchDrawer.PositionBranch(itemNode.BranchToParentElement, node, 
                    itemNode, rootPos, itemRootPos); 
                
                ExecuteLayoutRecursive(item, itemRootPos, newLeft, newRight, level);                         
            }
        }

        private void UpdateGroupShape(NTreeNode<NewickNode> treeNode, Point rootPos,
            Point nodePos, double newLeft, double newRight)
        {
            NewickNode node = treeNode.Value();
            Path path = node.GroupInfo.Shape as Path;
            PathGeometry geometry = path.Data as PathGeometry;
            PathFigure figure = geometry.Figures[0];
            figure.StartPoint = rootPos;
            LineSegment lineSeg1 = geometry.Figures[0].Segments[0] as LineSegment;
            
            RotateTransform transform = new RotateTransform(newLeft, 
                rootPos.X, rootPos.Y);

            double multiplyRadius = MainWindow.ComputeGroupRadius(treeNode, radius,
                unit, bUseBranchLenghts); 
            //(bUseBranchLenghts && node.BranchLenght.HasValue) ? unit * node.BranchLenght.Value : radius;

            lineSeg1.Point = transform.Transform(
                new Point(rootPos.X + multiplyRadius, rootPos.Y));
            ArcSegment arcSeg = geometry.Figures[0].Segments[1] as ArcSegment;

            double size = Point.Subtract(lineSeg1.Point, arcSeg.Point).Length;
            arcSeg.Size = new Size(multiplyRadius, multiplyRadius);
            arcSeg.RotationAngle = (newLeft-newRight)/2;
            arcSeg.IsLargeArc = false;
            arcSeg.SweepDirection = SweepDirection.Clockwise;
            transform = new RotateTransform(newRight, rootPos.X, rootPos.Y);
            Point point = new Point(rootPos.X + multiplyRadius, rootPos.Y);
            arcSeg.Point = transform.Transform(point);
            LineSegment lineSeg2 = geometry.Figures[0].Segments[2] as LineSegment;
            lineSeg2.Point = rootPos;

        }
       

        /// <inheritdoc />
        public Shape CreateGroupShape(NewickNode node)
        {
            Path path = new Path();
            PathGeometry geometry = new PathGeometry();
            PathFigure figure = new PathFigure();
            figure.Segments.Add(new LineSegment());
            figure.Segments.Add(new ArcSegment());
            figure.Segments.Add(new LineSegment());
            geometry.Figures.Add(figure);
            path.Data = geometry;
            path.Stroke = new SolidColorBrush(node.GroupInfo.Color);
            path.Fill = new SolidColorBrush(node.GroupInfo.Color);
            path.Opacity = 0.2;
            return path;
        }

        bool bUseBranchLenghts = false;

        /// <inheritdoc />
        public bool UseBranchLenghts
        {
            get
            {
                return bUseBranchLenghts;
            }
            set
            {
                bUseBranchLenghts = value;
            }
        }        

        /// <inheritdoc />
        bool ISupportUnrooting.IsRooted
        {
            get
            {
                return _isRooted;
            }
            set
            {
                _isRooted = value;
            }
        }
    }
}

